/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package se.sll.engagemangsindex.ws;

import se.sll.engagemangsindex.exceptions.DuplicateUpdateException;
import se.sll.engagemangsindex.exceptions.InvalidRoutingException;
import java.util.*;
import javax.ejb.EJB;
import javax.inject.Inject;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPFactory;
import javax.xml.soap.SOAPFault;
import javax.xml.ws.soap.SOAPFaultException;
import lombok.extern.slf4j.Slf4j;
import riv.itintegration.engagementindex._1.EngagementTransactionType;
import se.sll.engagemangsindex.dto.EngagementTransactionTypeImpl;
import se.sll.engagemangsindex.dto.EngagementTypeImpl;
import se.sll.engagemangsindex.ejb.ConfigurationHolder;
import se.sll.engagemangsindex.ejb.EngagementEjb;
import se.sll.engagemangsindex.entity.Engagement;

/**
 *
 * @author khaleddaham
 */
@Slf4j
public class BaseUpdateImpl {

    @EJB
    private EngagementEjb engagementFacade;

    @Inject
    private ConfigurationHolder configurationHolder;

    /**
     *
     */
    public BaseUpdateImpl() {
    }

    /**
     * Helper method to throw SOAPFaults
     *
     * @param message
     * @throws SOAPException
     */
    protected void throwSoapFaultException(String message) throws SOAPFaultException {
        SOAPFactory factory;
        SOAPFault fault = null;
        try {
            factory = SOAPFactory.newInstance();
            fault = factory.createFault(message, new QName("http://foo", "UpdateWSImpl"));
            fault.setFaultActor("actor");
            fault.addDetail().addChildElement("test");
        } catch (SOAPException ex) {
        }
        throw new SOAPFaultException(fault);
    }

    /**
     * Check if all the engagements are unique, this relies on hashCode and equals in EngagementTypeImpl to return a unique id (as of now it should be registeredIdentification + serviceDomain +
     * categorization + logicalAddress + businessObjectIdentifier)
     *
     * @param engagements
     * @throws DuplicateUpdateException
     */
    protected void checkForDuplicateUpdates(List<EngagementTransactionTypeImpl> engagements) throws DuplicateUpdateException {
        EngagementTypeImpl e = getDuplicateIndex(engagements);
        if (e != null) {
            String message = "A duplicate update entry was found with unique id: "
                    + "registeredResidentIdentification(" + e.getRegisteredResidentIdentification() + "), "
                    + "serviceDomain(" + e.getServiceDomain() + "), "
                    + "categorization(" + e.getCategorization() + "), "
                    + "locgicalAddress(" + e.getLogicalAddress() + "), "
                    + "sourceSystem(" + e.getSourceSystem() + "), "
                    + "clinicalProcessInterestId(" + e.getClinicalProcessInterestId() + "), "
                    + "businessObjectIdentifier(" + e.getBusinessObjectInstanceIdentifier() + ")";
            throw new DuplicateUpdateException(message);
        }
    }

    /**
     * Checks if we have any duplicate objects in our collection, returns null if not.
     *
     * @param Collection<EngagementTransactionType>
     * @return
     */
    private static <T> EngagementTypeImpl getDuplicateIndex(Collection<EngagementTransactionTypeImpl> list) {
        Set<EngagementTypeImpl> set = new HashSet<EngagementTypeImpl>();
        for (EngagementTransactionTypeImpl t : list) {
            if (!set.add(t.getEngagement())) {
                return t.getEngagement();
            }
        }
        return null;
    }

    /**
     *
     * @param logicalAddress
     * @throws InvalidRoutingException
     */
    protected void checkForMatchingOwner(String logicalAddress) throws InvalidRoutingException {
        if (!isOwnerMatching(logicalAddress)) {
            String message = "Invalid routing. Logical address targets "
                    + logicalAddress + " but the responder is "
                    + this.getOwner();
            throw new InvalidRoutingException(message);
        }
    }

    protected List<EngagementTransactionTypeImpl> copy(List<EngagementTransactionType> engagementTransaction) {
        List<EngagementTransactionTypeImpl> newList = new ArrayList<EngagementTransactionTypeImpl>();
        for (EngagementTransactionType t : engagementTransaction) {
            EngagementTransactionTypeImpl newTransactionType = new EngagementTransactionTypeImpl();
            newTransactionType.setDeleteFlag(t.isDeleteFlag());
            newTransactionType.setEngagement(new EngagementTypeImpl(t.getEngagement()));
            newList.add(newTransactionType);
        }
        return newList;
    }

    /**
     *
     * @param logicalAddress
     * @return
     */
    protected boolean isOwnerMatching(String logicalAddress) {
        if (this.getOwner().equals(logicalAddress)) {
            return true;
        }
        return false;
    }

    /**
     *
     * @param EngagementTypeImpl
     */
    protected void removeEngagement(EngagementTypeImpl engagementTypeImpl) {
        log.debug("Removing engagement: {}", engagementTypeImpl.getRegisteredResidentIdentification());
        engagementFacade.remove(EngagementHelper.toEngagement(engagementTypeImpl));
    }

    /**
     * @param EngagementTypeImpl
     */
    protected void addEngagement(EngagementTypeImpl engagementTypeImpl) {
        log.debug("Adding engagement: {}", engagementTypeImpl.getRegisteredResidentIdentification());
        engagementFacade.update(EngagementHelper.toEngagement(engagementTypeImpl));
    }

    /**
     *
     * @param registeredResidentIdentification
     * @param serviceDomain
     * @param categorization
     * @param logicalAddress
     * @param businessObjectInstanceIdentifier
     * @param owner
     * @return
     */
    public boolean hasExistingEngagementWithDifferentOwner(Engagement engagement) {
        if (engagementFacade.hasEngagement(engagement)) {
            Engagement existing = engagementFacade.findByUniqueId(engagement);
            if (existing.getOwner() != null) {
                return !existing.getOwner().equals(engagement.getOwner());
            }
        }
        return false;
    }

    /**
     *
     * @return
     */
    protected String getOwner() {
        return configurationHolder.getOwner();
    }

    /**
     *
     * @return
     */
    protected EngagementEjb getEngagementEjb() {
        return this.engagementFacade;
    }

}